package com.cskaoyan.javase.object._1getclass;

/**
 * 方法声明为：
 * public final native Class<?> getClass();
 * 解释：
 *      1.首先它的访问权限修饰符是public，访问权限问题不用考虑。
 *      2.final修饰它，表示它无法被重写。
 *      3.native表示该方法是一个本地方法，指的是Java调用其它语言（主要是C/C++）的实现来完成功能的方法。
 *          本地方法不需要方法体，我们也不会考虑它的实现细节。
 *      4.Class<?>是返回值类型，表示该方法需要返回一个Class对象。
 *          其中<?>表示泛型，关于泛型后面会详细讲，这里我们先忽略它，并不影响方法调用结果。
 *          Java的泛型非常重要,只要是使用集合的场合,几乎必定使用泛型
 *          而Java的集合是开发中必用的容器
 *
 *          Java的泛型和C++的泛型不同,Java的泛型是"伪泛型",因为它是假的
 *          Java的泛型不影响程序执行,只影响代码编译
 *
 *      5.该方法不需要任何参数。
 *
 * 方法的作用:
 *      通过一个本地方法的实现，去获取Class对象。
 *
 * 所以接下来的问题，主要是：
 *      1.什么是Class对象？
 *      2.Class类是什么？它又有什么作用？
 * 接下来，我们就主要来研究一下Class类和它的对象。
 *
 *
 * Class对象:
 * 为了满足程序员在程序的运行期间,动态获取类型信息的需求,
 * JVM在类加载某个类的同时(具体是在加载的时期)，会在堆上会自动创建一个该类的运行时类对象，也就是这个类的Class对象。
 * Class对象当中，包含了该类的所有类型信息（比如类名是啥，有哪些方法、变量、构造器等）
 * 于是程序员在程序的运行时期，只需要获取该类的Class对象，就能够获取类型信息了。
 *
 * 而getClass方法就是获取类的运行时类对象(Class对象)的一种手段.
 *
 * 这就是Class对象的作用，也叫运行时类对象。
 * 运行时类对象,指的是这个类,在程序运行期间,封装了该类的类型信息
 * 比如Student类的运行时类对象,就封装了Student类的信息,在程序的运行期间就可以获取类型信息,这就是Class对象
 *
 * 注意事项:
 *      1.Class对象究竟是谁创建的,是不是getClass方法创建的?
 *          JVM在类加载某个类的同时,在堆上创建的,和getClass方法没有任何关系
 *      2.为什么getClass方法是本地方法?
 *          Java方法本身做不到这个事情: 从堆上获取一个没有引用指向的对象,返回引用
 *      3.类加载只有一次,所以某个类的Class对象,也只有一个,是唯一的
 *          所以:
 *          相同类型的对象,getClass方法的结果用"=="连接返回true
 *          不同类型的对象,getClass方法的结果用"=="连接返回false
 *          这点其实就指出了getClass方法在Java中,最常见的用途:
 *              用来判断两个引用所指向的对象,是不是相同类型的对象
 *              如果是,返回true
 *              不是,返回false
 *      4.(扩展)
 *          获取某个类的Class对象后,就可以在程序的运行期间,动态的完成对该类的操作(比如创建对象,赋值,调用方法等等)
 *          这其实就是Java中的反射技术,反射实际上就是Java Class类型的方法调用
 *          反射可以破解私有权限,配合Class对象获取类型信息,反射在Java中就是"为所欲为"
 *
 *      5.(扩展)
 *          泛型,建议今天,大家不要加出来,因为会影响"==",影响代码编译
 *
 *
 * @since 11:00
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s = new Student();
        Class stuClazz = s.getClass();
        Student s2 = new Student();
        Class stuClazz2 = s2.getClass();
        System.out.println(stuClazz == stuClazz2);
        Teacher t = new Teacher();
        Class teacherClazz = t.getClass();
        System.out.println(teacherClazz == stuClazz);

        System.out.println("------------------");
        System.out.println(judgeObjectType(t, s));
        System.out.println(judgeObjectType(s, s2));

        System.out.println("-------------------------");
        // 简单演示一下Class对象的使用
        // 获取类的全限定类名
        System.out.println(stuClazz.getName());
        // 获取类的简单类名
        System.out.println(stuClazz.getSimpleName());

        System.out.println("-----------------------------");
        Class<? extends Teacher> clazz1 = t.getClass();
        Class<? extends Student> clazz2 = s.getClass();
        // 泛型影响代码编译,所以Class<? extends Teacher>和Class<? extends Student>是完全不同的两种数据类型
        // System.out.println(clazz1 == clazz2);

        Object o1 = s;
        Object o2 = t;
        Class<?> clazz3 = o1.getClass();
        Class<?> clazz4 = o2.getClass();
        System.out.println(clazz3 == clazz4);
    }

    /**
     * 判断两个引用所指向的对象,是不是相同类型的对象
     *      如果是,返回true
     *      不是,返回false
     * @param o1
     * @param o2
     * @return boolean
     * @author wuguidong@cskaoyan.onaliyun.com
     * @since 2022/07/19 11:19
     */
    public static boolean judgeObjectType(Object o1, Object o2) {
        return o1.getClass() == o2.getClass();
    }
}

class Student {
}

class Teacher {
}